#include "vpxencoder.h"


VpxEncoder::VpxEncoder(QObject *parent) : QObject(parent),
    min_quntizer(4),
    max_quntizer(36),
    target_bitrate(500),
    timebase_den(25),
    dropframe_thresh(25),
    undershoot_pct(95),
    buf_initial_sz(4000),
    buf_optimal_sz(5000),
    rc_buf_sz(6000)
{
    bytesIvf.clear() ;
}
VpxEncoder::~VpxEncoder()
{

}
void VpxEncoder::putToEncoder(int pox,int poy,const QImage &image,int num)
{   
    bytesYUVout.clear();
    //转换
    convertToYUV(image,bytesYUVout) ;
    //清理
    bytesIvf.clear();
    //编码
    this->encodeVpxData(image.width(),image.height(),bytesYUVout,bytesIvf);
    //发送
    emit sendEncodeImageData(pox,poy,image.width(),image.height(),bytesIvf,num);

}
int VpxEncoder::encodeVpxData(int width ,int height, QByteArray &bArray_in, QByteArray &bArray_out) {
    QBuffer infile(&bArray_in) ;
    if (!( infile.open(QIODevice::ReadOnly))){
        qFatal("Failed to  open device");
    }
    ret = vpx_codec_enc_config_default(interface, &cfg, 0);
    if(ret) {
        qFatal("Failed to get config: %s\n", vpx_codec_err_to_string(ret));
        return -1;
    }
    cfg.g_w = width;
    cfg.g_h = height;
    cfg.rc_target_bitrate =target_bitrate;
    cfg.g_timebase.num = 1;
    cfg.g_timebase.den = timebase_den;
    cfg.rc_undershoot_pct = undershoot_pct;
    cfg.rc_min_quantizer = min_quntizer;
    cfg.rc_max_quantizer = max_quntizer;
    cfg.kf_mode = vpx_kf_mode::VPX_KF_DISABLED;
    cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;
    cfg.rc_end_usage = vpx_rc_mode::VPX_CBR;
    cfg.g_threads = 1;
    cfg.rc_buf_initial_sz = buf_initial_sz;
    cfg.rc_buf_optimal_sz = buf_optimal_sz;
    cfg.rc_buf_sz = rc_buf_sz;

    if(vpx_codec_enc_init(&codec, interface, &cfg, 0)){
        qFatal("Failed to initialize encoder\n");
        return -1;
    }
    if (vpx_codec_control(&codec, VP8E_SET_CPUUSED, 6)){
        qFatal("Failed to vpx_codec_control");
    }
    y_size=cfg.g_w*cfg.g_h;

    if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)){
        qFatal("Fail to allocate image\n");
        return -1;
    }
    frame_avail = 1;
    got_data = 0;
    while(frame_avail ||got_data) {
        vpx_codec_iter_t iter = NULL;
        const vpx_codec_cx_pkt_t *pkt;
        if(infile.read((char*)raw.planes[0], 1*y_size*3/2)!=y_size*3/2){
            frame_avail=0;
        }
        if(frame_avail){
            ret=vpx_codec_encode(&codec,&raw,frame_cnt,1,flags,VPX_DL_REALTIME);
        }else{
            ret=vpx_codec_encode(&codec,NULL,frame_cnt,1,flags,VPX_DL_REALTIME);
        }
        if(ret){
            qFatal("Failed to encode frame\n");
            return -1;
        }
        got_data = 0;
        while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) {
            got_data = 1;
            switch(pkt->kind) {
            case VPX_CODEC_CX_FRAME_PKT:
                write_ivf_frame_header(bArray_out, pkt);
                bArray_out.append((char*)pkt->data.frame.buf, 1* pkt->data.frame.sz);
                break;
            default:
                break;
            }
        }
        frame_cnt++;
    }
    write_ivf_file_header(bArray_out, &cfg, frame_cnt-1);

    infile.close();
    vpx_img_free(&raw);
    vpx_codec_destroy(&codec);
    return 0;
}
//输入image,输出byteArray
void VpxEncoder::convertToYUV(const QImage &image,QByteArray &byteArray){
    //source-size
    size_t width = image.width();//图像宽度（像素）
    size_t height = image.height();//图像高度（像素）
    //图片指针
    uint8_t *rgbaBuffer = (uint8_t*)image.bits();
    //大小
    int yuvBufSize = width * height * 3 / 2;
    //开辟
    uint8_t* yuvBuf= new uint8_t[yuvBufSize];
    //source-stride
    int Dst_Stride_Y = width;
    const int uv_stride = (width+1) / 2;
    //source-length
    const int y_length = width * height;
    int uv_length = uv_stride * ((height+1) / 2);
    //source-data
    unsigned char *Y_data_Dst = yuvBuf;
    unsigned char *U_data_Dst = yuvBuf + y_length;
    unsigned char *V_data_Dst = U_data_Dst + uv_length;
    //BGRAToI420, 内存顺序是BGRA,所以用方法得反过来ARGB
    libyuv::ARGBToI420(rgbaBuffer,
                       width * 4,
                       Y_data_Dst, Dst_Stride_Y,
                       U_data_Dst, uv_stride,
                       V_data_Dst, uv_stride,
                       width, height);

    byteArray.append((char*)yuvBuf,yuvBufSize);
    delete[] yuvBuf;
}

void VpxEncoder::mem_put_le16(char *mem, unsigned int val) {
    mem[0] = val;
    mem[1] = val>>8;
}

void VpxEncoder::mem_put_le32(char *mem, unsigned int val) {
    mem[0] = val;
    mem[1] = val>>8;
    mem[2] = val>>16;
    mem[3] = val>>24;
}
void VpxEncoder::write_ivf_file_header(QByteArray &outfile,
                                       const vpx_codec_enc_cfg_t *cfg,
                                       int frame_cnt) {
    char header[32];

    if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS)
        return;
    header[0] = 'D';
    header[1] = 'K';
    header[2] = 'I';
    header[3] = 'F';
    mem_put_le16(header+4,  0);                   /* version */
    mem_put_le16(header+6,  32);                  /* headersize */
    mem_put_le32(header+8,  fourcc);              /* headersize */
    mem_put_le16(header+12, cfg->g_w);            /* width */
    mem_put_le16(header+14, cfg->g_h);            /* height */
    mem_put_le32(header+16, cfg->g_timebase.den); /* rate */
    mem_put_le32(header+20, cfg->g_timebase.num); /* scale */
    mem_put_le32(header+24, frame_cnt);           /* length */
    mem_put_le32(header+28, 0);                   /* unused */

    outfile.insert(0,QByteArray(header,32));
}


void VpxEncoder::write_ivf_frame_header(QByteArray &data,
                                        const vpx_codec_cx_pkt_t *pkt)
{
    char             header[12];
    vpx_codec_pts_t  pts;

    if(pkt->kind != VPX_CODEC_CX_FRAME_PKT)
        return;

    pts = pkt->data.frame.pts;
    mem_put_le32(header, pkt->data.frame.sz);
    mem_put_le32(header+4, pts&0xFFFFFFFF);
    mem_put_le32(header+8, pts >> 32);

    data.append(header,1*12);
}


